/*
  S.M.A.C.K - An operating system kernel
  Copyright (C) 2010,2011 Mattias Holm and Kristian Rietveld
  For licensing and a full list of authors of the kernel, see the files
  COPYING and AUTHORS.
*/

#ifndef KERNEL__ARM__CPU_H
#define KERNEL__ARM__CPU_H

/*
  Common ARM Register access macros and assembly snippets
*/

#define SCTLR_TE   (1 << 30)
#define SCTLR_AFE  (1 << 29)
#define SCTLR_TRE  (1 << 28)
#define SCTLR_NMFI (1 << 27)
#define SCTLR_EE   (1 << 25)
#define SCTLR_VE   (1 << 24)
#define SCTLR_U    (1 << 22)
#define SCTLR_FI   (1 << 21)
#define SCTLR_HA   (1 << 17)
#define SCTLR_RR   (1 << 14)
#define SCTLR_V    (1 << 13)
#define SCTLR_I    (1 << 12)
#define SCTLR_Z    (1 << 11)
#define SCTLR_SW   (1 << 10)
#define SCTLR_B    (1 << 7)
#define SCTLR_C    (1 << 2)
#define SCTLR_A    (1 << 1)
#define SCTLR_M    (1)


static inline void
set_sctlr(uint32_t set, uint32_t clear)
{
  __asm__ volatile (
    "mrc p15, 0, r4, c1, c0, 0          @ Read SCTLR\n"
    "bic r4, r4, %[clear]               @ Clear bits\n"
    "orr r4, r4, %[set]                 @ Set bits\n"
    "mcr p15, 0, r4, c1, c0, 0          @ Write SCTLR\n"
    : // No outs
    : [clear] "r" (clear), [set] "r" (set)
    : "r4"
    );
}

static inline uint32_t
read_sctlr(void)
{
  uint32_t sctlr = 0;
  __asm__ volatile (
    "mrc p15, 0, %[sctlr], c1, c0, 0          @ Read SCTLR\n"
    : [sctlr] "=r" (sctlr) // Outs
    : // Ins
    );
  return sctlr;
}


static inline void
set_tbr0(uint32_t ttbr0)
{
  /* Set TTBR0 and immediately flush the TLB without executing a
   * function call.
   */
  __asm__ volatile (
    "isb\n"
    "mcr p15, 0, %[ttbr_val], c2, c0, 0   @ Write TBR0\n"
    "mov r4, #0\n"
    "mcr p15, 0, r4, c8, c5, 0          @ Invalidate entire iTLB\n"
    "mcr p15, 0, r4, c8, c6, 0          @ Invalidate entire dTLB\n"
    "mcr p15, 0, r4, c8, c7, 0          @ Invalidate entire uTLB\n"
    "dsb\n"
    "isb                                @ Instruction sync. barrier\n"
    : // No outs
    : [ttbr_val] "r" (ttbr0) // Uses ttbr0 arg for register
    : "r4"
    );
}

static inline uint32_t
read_tbr0(void)
{
  uint32_t reg;

  __asm__ volatile (
    "mrc p15, 0, %[reg], c2, c0, 0\n"
    : [reg] "=r" (reg) // Out
    :  // No ins
    );
  return reg;
}

static inline void
set_tbr0_2(uint32_t ttbr0)
{
  /* Set TTBR0 and immediately flush the TLB without executing a
   * function call.
   */
  __asm__ volatile (
    "mcr p15, 0, %[ttbr_val], c2, c0, 0   @ Write TBR0\n"
    : // No outs
    : [ttbr_val] "r" (ttbr0) // Uses ttbr0 arg for register
    );
}



static inline void
set_tbr1(uint32_t ttbr1)
{
  __asm__ volatile (
    "mcr p15, 0, %[ttbr_val], c2, c0, 1   @ Write TBR1\n\n"
    : // No outs
    : [ttbr_val] "r" (ttbr1) // Uses ttbr1 arg for register
    );
}
static inline uint32_t
read_tbr1(void)
{
  uint32_t reg;

  __asm__ volatile (
    "mrc p15, 0, %[reg], c2, c0, 1\n"
    : [reg] "=r" (reg) // Out
    :  // No ins
    );
  return reg;
}



#define PRRR_NOS7 (1<<31)
#define PRRR_NOS6 (1<<30)
#define PRRR_NOS5 (1<<29)
#define PRRR_NOS4 (1<<28)
#define PRRR_NOS3 (1<<27)
#define PRRR_NOS2 (1<<26)
#define PRRR_NOS1 (1<<25)
#define PRRR_NOS0 (1<<24)

#define PRRR_NS1 (1<<19)
#define PRRR_NS0 (1<<18)
#define PRRR_DS1 (1<<17)
#define PRRR_DS0 (1<<16)

#define PRRR_TR_STRONGLY_ORDERED 0
#define PRRR_TR_DEVICE 1
#define PRRR_TR_NORMAL_MEMORY 2

#define PRRR_TR7 14
#define PRRR_TR6 12
#define PRRR_TR5 10
#define PRRR_TR4  8
#define PRRR_TR3  6
#define PRRR_TR2  4
#define PRRR_TR1  2
#define PRRR_TR0  0

static inline void
set_prrr(uint32_t prrr)
{
  __asm__ volatile (
    "mcr p15, 0, %[prrr_val], c10, c2, 0   @ Write PRRR\n\n"
    : // No outs
    : [prrr_val] "r" (prrr)
    );
}

static inline uint32_t
read_prrr(void)
{
  uint32_t reg;

  __asm__ volatile (
    "mrc p15, 0, %[reg], c10, c2, 0\n"
    : [reg] "=r" (reg) // Out
    :  // No ins
    );
  return reg;
}

#define NMRR_NON_CACHABLE 0
#define NMRR_WB_WA 1
#define NMRR_WT_NWA 2
#define NMRR_WB_NWA 3

// The following defines the shift distance to the parts of the registers
#define NMRR_OR7 30
#define NMRR_OR6 28
#define NMRR_OR5 26
#define NMRR_OR4 24
#define NMRR_OR3 22
#define NMRR_OR2 20
#define NMRR_OR1 18
#define NMRR_OR0 16

#define NMRR_IR7 14
#define NMRR_IR6 12
#define NMRR_IR5 10
#define NMRR_IR4  8
#define NMRR_IR3  6
#define NMRR_IR2  4
#define NMRR_IR1  2
#define NMRR_IR0  0

static inline void
set_nmrr(uint32_t nmrr)
{
  __asm__ volatile (
    "mcr p15, 0, %[nmrr_val], c10, c2, 1   @ Write NMRR\n\n"
    : // No outs
    : [nmrr_val] "r" (nmrr)
    );
}
static inline uint32_t
read_nmrr(void)
{
  uint32_t reg;

  __asm__ volatile (
    "mrc p15, 0, %[reg], c10, c2, 1\n"
    : [reg] "=r" (reg) // Out
    :  // No ins
    );
  return reg;
}

#define TTBCR_PD1 (1 << 5)
#define TTBCR_PD0 (1 << 4)

static inline void
set_ttbcr2(uint32_t set, uint32_t clear)
{
  __asm__ volatile (
    "mrc p15, 0, r4, c2, c0, 2          @ Read TTBCR\n"
    "bic r4, r4, %[clear]               @ Clear bits\n"
    "orr r4, r4, %[set]                 @ Set bits\n"
    "mcr p15, 0, r4, c2, c0, 2          @ Write TTBCR\n"
    : // No outs
    : [clear] "r" (clear), [set] "r" (set)
    : "r4"
    );
}


static inline void
set_ttbcr(uint32_t ttbcr)
{
  __asm__ volatile (
    "mcr p15, 0, %[ttbcr_val], c2, c0, 2   @ Write TBRCR\n\n"
    : // No outs
    : [ttbcr_val] "r" (ttbcr) // Uses ttbr1 arg for register
    );
}

static inline uint32_t
read_ttbcr(void)
{
  uint32_t reg;

  __asm__ volatile (
    "mrc p15, 0, %[reg], c2, c0, 2\n"
    : [reg] "=r" (reg) // Out
    :  // No ins
    );
  return reg;
}


static inline void
set_domain(uint32_t domvalue)
{
  __asm__ volatile (
    "mov r4, %[dom_val]\n"
    "mcr p15, 0, r4, c3, c0, 0\n"
    : // No outs
    : [dom_val] "r" (domvalue) // Uses ttbr1 arg for register
    );
}

static inline uint32_t
read_midr(void)
{
  uint32_t midr;

  __asm__ volatile (
    "mrc p15, 0, %[midr_reg], c0, c0, 0\n"
    : [midr_reg] "=r" (midr) // MIDR out
    :  // No ins
    );
  return midr;
}

static inline uint32_t
read_mpidr(void)
{
  uint32_t mpidr;

  __asm__ volatile (
    "mrc p15, 0, %[mpidr], c0, c0, 5\n"
    : [mpidr] "=r" (mpidr) // MIDR out
    :  // No ins
    );
  return mpidr;
}

#define CSIDR_LNSZ_MASK    0x00000007
#define CSIDR_ASSOC_MASK   0x00001ff8
#define CSIDR_NUMSETS_MASK 0x0fffe000

#define CSIDR_LNSZ_SHIFT    0
#define CSIDR_ASSOC_SHIFT   3
#define CSIDR_NUMSETS_SHIFT 13

#define CSIDR_WA      0x10000000
#define CSIDR_RA      0x20000000
#define CSIDR_WB      0x40000000
#define CSIDR_WT      0x80000000

static inline uint32_t
read_csidr(void)
{
  uint32_t csidr;
  __asm__ volatile (
    "mrc p15, 1, %[csidr], c0, c0, 0\n"
    : [csidr] "=r" (csidr) // MIDR out
    :  // No ins
    );
  return csidr;
}


static inline void
write_cssr(uint32_t cssr)
{
  __asm__ volatile (
    "mcr p15, 2, %[cssr], c0, c0, 0\n"
    : // No outs
    : [cssr] "r" (cssr) // Ins
    );
}



static inline uint32_t
read_dfsr(void)
{
  uint32_t reg;

  __asm__ volatile (
    "mrc p15, 0, %[reg], c5, c0, 0\n"
    : [reg] "=r" (reg) // Out
    );
  return reg;
}

static inline uint32_t
read_dfar(void)
{
  uint32_t reg;

  __asm__ volatile (
    "mrc p15, 0, %[reg], c6, c0, 0\n"
    : [reg] "=r" (reg) // Out
    );
  return reg;
}
static inline uint32_t
read_ifsr(void)
{
  uint32_t reg;

  __asm__ volatile (
    "mrc p15, 0, %[reg], c5, c0, 1\n"
    : [reg] "=r" (reg) // Out
    );
  return reg;
}

static inline uint32_t
read_ifar(void)
{
  uint32_t reg;

  __asm__ volatile (
    "mrc p15, 0, %[reg], c6, c0, 2\n"
    : [reg] "=r" (reg) // Out
    );
  return reg;
}


static inline void
dsb(void)
{
  __asm__ volatile (
    "dsb          @ Data Sync Barrier\n"
    );
}


static inline void
isb(void)
{
  __asm__ volatile (
    "isb          @ Instruction Sync Barrier\n"
    );
}





#define ACR_L2EN 2

static inline void
set_acr(uint32_t set, uint32_t clear)
{
  __asm__ volatile (
    "mrc p15, 0, r4, c1, c0, 1          @ Read SCTLR\n"
    "bic r4, r4, %[clear]               @ Clear bits\n"
    "orr r4, r4, %[set]                 @ Set bits\n"
    "mcr p15, 0, r4, c1, c0, 1          @ Write SCTLR\n"
    : // No outs
    : [clear] "r" (clear), [set] "r" (set)
    : "r4"
    );
}



#endif /* !KERNEL__ARM__CPU_H */
